;[]------------------------------------------------------------[]
;|      C0D.ASM -- Start Up Code For Windows DLLs               |
;[]------------------------------------------------------------[]

;
;       C/C++ Run Time Library - Version 5.0
; 
;       Copyright (c) 1991, 1992 by Borland International
;       All Rights Reserved.
; 

                locals

                __C0__ = 1
include         RULES.ASI

                ASSUME CS:_TEXT, DS:DGROUP

                public  __acrtused              ;satisfy MS for now
__acrtused      equ     0


                extrn LIBMAIN:far       ;the C routine to be called
                extrn LOCALINIT:far     ;Windows heap init routine
                extrn LOCKSEGMENT:far
                extrn UNLOCKSEGMENT:far
                extrn GETWINFLAGS:far
                extrn __WEP:far
                extrn __cexit:DIST
                extrn __setupio:near    ;required!

                public LibEntry         ;Entry point for the DLL
                publicdll WEP

NULL            segment
                db      16 dup (?)
                ends

_CVTSEG         segment
                public __RealCvtVector
__RealCvtVector label word
                ends

_SCNSEG         segment
                public __ScanTodVector
__ScanTodVector label word
                ends

_FPSEG          segment
                public __FPVector
__FPVector      dd      0
                ends

_DATA           segment
                public _errno
_errno          dw      0
                public __protected
__protected     dw      0
                public __8087
__8087          dw      0
                public __StartTime
__StartTime     dd      0
                public __version
__version       label word
                public __osversion
__osversion     label word
                public __osmajor
__osmajor       db      0
                public __osminor
__osminor       db      0
                public __osmode         ;Used for OS/2 protected mode by MS,
__osmode        db      0               ;currently set to 0 under Windows
                public __hInstance
__hInstance     dw      0
                public __WinAllocFlag   ;Used by malloc for additional flags
__WinAllocFlag  dw      0               ;to pass to GlobalAlloc (used in DLLs)
                public __LockWIN87EM    ;Used do lock down WIN87EM to avoid
__LockWIN87EM   dw      1               ;DLL unload ordering problem

_abend          dw      1               ;If LibEntry is called gets set to
                                        ;normal state (0).  If it is 1 then
                                        ;exit routines are not performed
                                        ;Gets set to 1 if DLL is terminated
                                        ;by a call to abort() or _exit().

CopyRight       db      'Borland C++ - Copyright 1991 Borland Intl.',0
                ends

_TEXT           segment

LibEntry        proc far
                mov     __hInstance, di ;save SI and DI
                push    si

                push    di              ;handle of the module instance
                push    ds              ;library data segment
                push    cx              ;heap size
                push    es              ;command line segment
                push    si              ;command line offset

                ;if we have some heap then initialize it
                jcxz    @@Init          ;jump if no heap specified

                ;call the Windows function LocalInit() to set up the heap
                ;LocalInit((LPSTR)start, WORD cbHeap);

                push    ds              ;Heap segment
                xor     ax,ax
                push    ax              ;Heap start offset in segment
                push    cx              ;Heap end offset in segment
                call    LOCALINIT
                xchg    ax,cx
                jcxz    @@JmpExit       ;quit if it failed
                jmp     short @@Init
@@JmpExit:      jmp     @@Exit

@@Init:

IF LDATA EQ false
                mov     ax,-1
                push    ax
                call    LOCKSEGMENT
ENDIF

                ;Clear _BSS, uninitialized data area

                xor     ax, ax
                push    ds
                pop     es
                mov     di,offset DGROUP:BeginBSS
                mov     cx,offset DGROUP:EndBSS
                sub     cx,di
                cld
                rep
                stosb

;Save start time of DLL

                mov     ah, 0
                int     1ah             ; get current BIOS time in ticks
                mov     word ptr __StartTime,dx ; save it for clock() fn
                mov     word ptr __StartTime+2,cx
                or      al,al                   ; was midnight flag set?
                jz      @@NotMidnight
                mov     ax,40h                  ; set BIOS midnight flag
                mov     es,ax                   ;  at 40:70
                mov     bx,70h
                mov     byte ptr es:[bx],1
@@NotMidnight:

;Determine DOS version

                mov     ah, 30h
                int     21h
                mov     __version, ax   ; save minor and major revision

;Determine whether we are in protected mode

                call    GETWINFLAGS
                test    ax,1            ; WF_PMODE = 1
                jz      @@realmode      ; Note:  GETWINFLAGS returns a long,
                                        ; so if WF_PMODE changed it could be
                                        ; in the high word.
                mov     __protected, 8  ; Eight is for convenience.
@@realmode:

;Test for 8087 presence

                test    ax,0400h        ; WF_8087 = 0x0400
                jz      @@no8087
                mov     __8087, 1
@@no8087:

;Call our initialization functions, including C++ static constructors.

                mov     ax,ds
                mov     es,ax
                mov     si,offset DGROUP:InitStart      ;si = start of table
                mov     di,offset DGROUP:InitEnd        ;di = end of table

                mov     __WinAllocFlag, 2000h           ; GMEM_SHARE
                call    Initialize
                mov     __WinAllocFlag, 0
                mov     _abend, 0                       ; Set LibEntry called

                ;invoke the C routine to do any special initialization
@@Main:         call    LIBMAIN         ;invoke the 'C' routine (result in AX)
                mov     di, __hInstance ;restore SI and DI
                pop     si
                ret

@@Exit:         mov ax, 0               ;set return code
                pop si                  ;remove arguments to LIBMAIN
                pop es                  ;  since we didn't call it.
                pop cx
                pop ds
                pop di
                pop si                  ;restore saved SI.  DI is restored
                ret                     ;  by removing arguments to LIBMAIN
                endp

;---------------------------------------------------------------------------
;       _cleanup()      call all #pragma exit cleanup routines.
;       _checknull()    check for null pointer zapping copyright message
;       _terminate(exitcode, quick)     exit program with error code
;       _restorezero()  restore interrupt vectors
;
;       These functions are called by exit(), _exit(), _cexit(),
;       and _c_exit().
;---------------------------------------------------------------------------

;       Call cleanup routines

__cleanup       PROC    DIST
                PUBLIC  __cleanup

                mov     ax,ds
                mov     es,ax
                push    si
                push    di
                mov     si,offset DGROUP:ExitStart
                mov     di,offset DGROUP:ExitEnd
                call    Cleanup
                pop     di
                pop     si
                ret
__cleanup       ENDP

;       Check for null pointers before exit.  NO-OP on Windows.

__checknull     PROC    DIST
                PUBLIC  __checknull
                ret
__checknull     ENDP

;       Restore grabbed interrupt vectors.  NO-OP on Windows.

__restorezero     PROC    DIST
                PUBLIC  __restorezero
                ret
__restorezero     ENDP

;       Exit to DOS
;
; Usage:        void _terminate(int exitcode, int quick);

__terminate     PROC    DIST
                PUBLIC  __terminate

                mov     bp,sp
                mov     al,[bp+cPtrSize]        ; get exitcode
                mov     ah,4ch
                int     21h
                ret
                endp

WEP             proc    windows pascal far nParam:WORD
                push    si
                push    di

                cmp     _abend, 0
                jne     @@error
                push    nParam
                call    __WEP
                push    ax
                call    __cexit         ; perform cleanup without exiting

@@unlock:

IF LDATA EQ false
                mov     ax,-1
                push    ax
                call    UNLOCKSEGMENT
ENDIF

                pop     ax
                pop     di
                pop     si
                ret

@@error:
                push    1
                jmp     @@unlock

                endp

;       Return default data segment in AX

__GetDGROUP     PROC    FAR
                PUBLIC  __GetDGROUP
                mov     ax, DGROUP
                ret
                endp

;------------------------------------------------------------------
;  Loop through a startup/exit (SE) table,
;  calling functions in order of priority.
;  ES:SI is assumed to point to the beginning of the SE table
;  ES:DI is assumed to point to the end of the SE table
;  First 64 priorities are reserved by Borland
;------------------------------------------------------------------
PNEAR           EQU     0
PFAR            EQU     1
NOTUSED         EQU     0ffh

SE              STRUC
calltype        db      ?                       ; 0=near,1=far,ff=not used
priority        db      ?                       ; 0=highest,ff=lowest
addrlow         dw      ?
addrhigh        dw      ?
SE              ENDS

Initialize      proc near
@@Start:        mov     ax,100h                 ;start with lowest priority
                mov     dx,di                   ;set sentinel to end of table
                mov     bx,si                   ;bx = start of table

@@TopOfTable:   cmp     bx,di                   ;and the end of the table?
                je      @@EndOfTable            ;yes, exit the loop
                cmp     es:[bx.calltype],NOTUSED;check the call type
                je      @@Next
                mov     cl, es:[bx.priority]    ;move priority to CX
                xor     ch, ch
                cmp     cx,ax                   ;check the priority
                jae     @@Next                  ;too high?  skip
                mov     ax,cx                   ;keep priority
                mov     dx,bx                   ;keep index in dx
@@Next:         add     bx,SIZE SE              ;bx = next item in table
                jmp     @@TopOfTable

@@EndOfTable:   cmp     dx,di                   ;did we exhaust the table?
                je      @@Done                  ;yes, quit
                mov     bx,dx                   ;bx = highest priority item
                cmp     es:[bx.calltype],PNEAR  ;is it near or far?
                mov     es:[bx.calltype],NOTUSED;wipe the call type
                push    es                      ;save es
                je      @@NearCall

@@FarCall:      call    DWORD PTR es:[bx.addrlow]
                pop     es                      ;restore es
                jmp     short @@Start

@@NearCall:     call    WORD PTR es:[bx.addrlow]
                pop     es                      ;restore es
                jmp     short @@Start

@@Done:         ret
                endp

Cleanup         proc near
@@Start:        mov     ah,0                    ;start with highest priority
                mov     dx,di                   ;set sentinel to end of table
                mov     bx,si                   ;bx = start of table

@@TopOfTable:   cmp     bx,di                   ;and the end of the table?
                je      @@EndOfTable            ;yes, exit the loop
                cmp     es:[bx.calltype],NOTUSED;check the call type
                je      @@Next
                cmp     es:[bx.priority],ah     ;check the priority
                jb      @@Next                  ;too low?  skip
                mov     ah,es:[bx.priority]     ;keep priority
                mov     dx,bx                   ;keep index in dx
@@Next:         add     bx,SIZE SE              ;bx = next item in table
                jmp     @@TopOfTable

@@EndOfTable:   cmp     dx,di                   ;did we exhaust the table?
                je      @@Done                  ;yes, quit
                mov     bx,dx                   ;bx = highest priority item
                cmp     es:[bx.calltype],PNEAR  ;is it near or far?
                mov     es:[bx.calltype],NOTUSED;wipe the call type
                push    es                      ;save es
                je      @@NearCall

@@FarCall:      call    DWORD PTR es:[bx.addrlow]
                pop     es                      ;restore es
                jmp     short @@Start

@@NearCall:     call    WORD PTR es:[bx.addrlow]
                pop     es                      ;restore es
                jmp     short @@Start

@@Done:         ret
                endp

                ends
                end LibEntry
